using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Factory
{
    public class Grid<TGridObject> 
    {
        public event EventHandler<GridObjectChangedEventArgs> OnGridObjectChanged; 
        public class GridObjectChangedEventArgs : EventArgs 
        {
            public int x;
            public int y;
        }
        int width_;
        int height_;
        int cell_size_;
        TGridObject[,] grid_array_;
        TextMesh[,] grid_text_;
        Vector3 origin_pos_;
        public Grid(int width, int height, int cell_size, Vector3 origin, Func<Grid<TGridObject>, int, int, TGridObject> creator)
        {
            width_ = width;
            height_ = height;
            cell_size_ = cell_size;
            grid_array_ = new TGridObject[width, height];
            grid_text_ = new TextMesh[width, height];
            origin_pos_ = origin;

            for (int i = 0; i < width; ++i)
            {
                for (int j = 0; j < height; ++j)
                {
                    grid_array_[i, j] = creator(this, i, j);
                }
            }

            var offset = new Vector3(cell_size / 2, cell_size / 2);
            for (int i = 0; i < width; ++i)
            {
                for (int j = 0; j < height; ++j)
                {
                    Debug.Log($"{i} <=> {j}");
                    grid_text_[i, j] = Utils.CreatWorldText(null, grid_array_[i, j].ToString(), GetWorldPosition(i, j) + offset, Color.black);
                    Debug.DrawLine(GetWorldPosition(i, j), GetWorldPosition(i + 1, j), Color.black, 100f);
                    Debug.DrawLine(GetWorldPosition(i, j), GetWorldPosition(i, j+1), Color.black, 100f);
                }
            }
            Debug.DrawLine(GetWorldPosition(0, height), GetWorldPosition(width, height), Color.black, 100f);
            Debug.DrawLine(GetWorldPosition(width, 0), GetWorldPosition(width, height), Color.black, 100f);

            OnGridObjectChanged += (object sender, GridObjectChangedEventArgs eventArgs) => {
                grid_text_[eventArgs.x, eventArgs.y].text = grid_array_[eventArgs.x, eventArgs.y]?.ToString();
            };

        }

        public Vector3 GetWorldPosition(int x, int y)
        {
            return new Vector3(x, y) * cell_size_ + origin_pos_;
        }

        private void GetXY(Vector3 wpos, out int x, out int y)
        {
            var pos = wpos - origin_pos_;
            x = Mathf.FloorToInt(pos.x / cell_size_);
            y = Mathf.FloorToInt(pos.y / cell_size_);
        }

        public int GetWidth()
        {
            return width_;
        }

        public int GetHeight()
        {
            return height_;
        }

        public int GetCellSize()
        {
            return cell_size_;
        }

        public void SetValue(int x, int y, TGridObject value)
        {
            if (x < 0 || x >= width_)
                return;

            if (y < 0 || y >= height_)
                return;

            grid_array_[x, y] = value;
            grid_text_[x, y].text = value.ToString();
            if (null != OnGridObjectChanged)
            {
                OnGridObjectChanged(this, new GridObjectChangedEventArgs { x = x, y = y });
            }
        }

        public void SetValue(Vector3 pos, TGridObject value)
        {
            int x, y;
            GetXY(pos, out x, out y);
            SetValue(x, y, value);
        }

        public void TriggerGridObjectChanged(int x, int y) {
            if (OnGridObjectChanged != null) OnGridObjectChanged(this, new GridObjectChangedEventArgs { x = x, y = y });
        }

        public TGridObject GetGridObject(int x, int y) 
        {
            if (x >= 0 && y >= 0 && x < width_ && y < height_) {
                return grid_array_[x, y];
            } else {
                return default(TGridObject);
            }
        }

        public TGridObject GetGridObject(Vector3 pos)
        {
            int x, y;
            GetXY(pos, out x, out y);
            return GetGridObject(x, y);
        }
    }
}
